package com.note.app.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.note.app.annotation.LogRecord;
import com.note.app.config.SentinelBlockHandler;
import com.note.app.dto.*;
import com.note.app.service.UserService;
import com.note.common.context.UserContext;
import com.note.common.vo.ResponseVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 用户控制器
 * 处理用户相关请求，包括注册、登录、获取用户信息等
 */
@Slf4j
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理", description = "用户注册、登录、信息管理等接口")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 用户注册
     *
     * @param registerDTO 注册信息
     * @return 注册结果
     */
    @PostMapping("/register")
    @Operation(summary = "用户注册", description = "通过用户名、密码、邮箱等信息注册新用户")
    @LogRecord(description = "用户注册", module = "用户管理", operationType = LogRecord.OperationType.CREATE)
    @SentinelResource(value = "userRegister",
            fallback = "fallBackHandle",
            fallbackClass = SentinelBlockHandler.class,
            blockHandler = "blockHandleException",
            blockHandlerClass = SentinelBlockHandler.class)
    public ResponseVo<Void> register(@RequestBody @Valid UserRegisterDTO registerDTO) {
        log.info("用户注册: {}", registerDTO.getUsername());
        return userService.register(registerDTO);
    }

    /**
     * 手机号验证码注册
     *
     * @param registerDTO 手机注册信息
     * @param request     HTTP请求
     * @return 注册结果，包含用户信息和token
     */
    @PostMapping("/mobile/register")
    @Operation(summary = "手机号验证码注册", description = "通过手机号、验证码和密码注册新用户，返回用户信息和访问令牌")
    @LogRecord(description = "手机号验证码注册", module = "用户管理", operationType = LogRecord.OperationType.CREATE)
    @SentinelResource(value = "mobileRegister",
            fallback = "fallBackHandle",
            fallbackClass = SentinelBlockHandler.class,
            blockHandler = "blockHandleException",
            blockHandlerClass = SentinelBlockHandler.class)
    public ResponseVo<UserDTO> mobileRegister(@RequestBody @Valid MobileRegisterDTO registerDTO,
                                              HttpServletRequest request) {
        // 设置登录IP
        registerDTO.setLoginIp(getClientIp(request));
        // 设置设备信息
        if (registerDTO.getDeviceInfo() == null) {
            registerDTO.setDeviceInfo(request.getHeader("User-Agent"));
        }

        log.info("手机号注册: {}", registerDTO.getMobile());
        return userService.mobileRegister(registerDTO);
    }

    /**
     * 用户登录
     *
     * @param loginDTO 登录信息
     * @return 登录结果，包含用户信息和token
     */
    @PostMapping("/login")
    @Operation(summary = "用户登录", description = "通过用户名和密码登录系统，返回用户信息和访问令牌")
    @LogRecord(description = "用户登录", module = "用户管理", operationType = LogRecord.OperationType.LOGIN)
    @SentinelResource(value = "userLogin",
            fallback = "fallBackHandle",
            fallbackClass = SentinelBlockHandler.class,
            blockHandler = "blockHandleException",
            blockHandlerClass = SentinelBlockHandler.class)
    public ResponseVo<UserDTO> login(@RequestBody @Valid UserLoginDTO loginDTO) {
        log.info("用户登录: {}", loginDTO.getUsername());
        return userService.login(loginDTO);
    }

    /**
     * 获取当前用户信息
     *
     * @return 用户信息
     */
    @GetMapping("/info")
    @Operation(summary = "获取当前用户信息", description = "获取当前登录用户的详细信息")
    @LogRecord(description = "获取当前用户信息", module = "用户管理", operationType = LogRecord.OperationType.QUERY)
    @SentinelResource(value = "getUserInfo",
            fallback = "fallBackHandle",
            fallbackClass = SentinelBlockHandler.class,
            blockHandler = "blockHandleException",
            blockHandlerClass = SentinelBlockHandler.class)
    public ResponseVo<UserDTO> getCurrentUser() {
        Long userId = UserContext.getUserId();
        log.info("获取当前用户信息: userId={}", userId);
        return userService.getUserById(userId);
    }

    /**
     * 根据用户ID获取用户信息
     *
     * @param userId 用户ID
     * @return 用户信息（不包含敏感数据）
     */
    @GetMapping("/{userId}")
    @Operation(summary = "根据用户ID获取用户信息", description = "根据用户ID获取用户的公开信息，不包含敏感数据")
    @Parameters({
            @Parameter(name = "userId", description = "用户ID", required = true, example = "1")
    })
    @LogRecord(description = "查询用户信息", module = "用户管理", operationType = LogRecord.OperationType.QUERY)
    public ResponseVo<UserDTO> getUserById(@PathVariable("userId") Long userId) {
        log.info("获取用户公开信息: userId={}", userId);
        return userService.getUserPublicInfo(userId);
    }

    /**
     * 更新用户信息
     *
     * @param updateDTO 更新信息
     * @return 更新结果
     */
    @PutMapping("/update")
    @Operation(summary = "更新用户信息", description = "更新当前登录用户的个人信息")
    @LogRecord(description = "更新用户信息", module = "用户管理", operationType = LogRecord.OperationType.UPDATE)
    public ResponseVo<Void> updateUser(@RequestBody @Valid UserUpdateDTO updateDTO) {
        Long userId = UserContext.getUserId();
        log.info("更新用户信息: userId={}, updateData={}", userId, updateDTO);
        return userService.updateUser(updateDTO, userId);
    }

    /**
     * 用户注销
     *
     * @return 注销结果
     */
    @PostMapping("/logout")
    @Operation(summary = "用户注销", description = "注销当前登录状态")
    @LogRecord(description = "用户注销", module = "用户管理", operationType = LogRecord.OperationType.LOGOUT)
    public ResponseVo<Void> logout() {
        Long userId = UserContext.getUserId();
        log.info("用户注销: userId={}", userId);
        return userService.logout(userId);
    }

    /**
     * 手机号验证码登录
     *
     * @param mobileLoginDTO 手机登录信息
     * @param request        HTTP请求
     * @return 登录结果，包含用户信息和token
     */
    @PostMapping("/mobile/login")
    @Operation(summary = "手机号验证码登录", description = "通过手机号和验证码登录系统，返回用户信息和访问令牌")
    @LogRecord(description = "手机号验证码登录", module = "用户管理", operationType = LogRecord.OperationType.LOGIN)
    public ResponseVo<UserDTO> mobileLogin(@RequestBody @Valid MobileLoginDTO mobileLoginDTO,
                                           HttpServletRequest request) {
        // 设置登录IP
        mobileLoginDTO.setLoginIp(getClientIp(request));
        // 设置设备信息
        if (mobileLoginDTO.getDeviceInfo() == null) {
            mobileLoginDTO.setDeviceInfo(request.getHeader("User-Agent"));
        }

        log.info("手机号登录: {}", mobileLoginDTO.getMobile());
        return userService.mobileLogin(mobileLoginDTO);
    }

    /**
     * 发送验证码
     *
     * @param codeDTO 验证码请求
     * @return 发送结果
     */
    @PostMapping("/verification/code")
    @Operation(summary = "发送验证码", description = "发送手机验证码，支持登录、注册、重置密码等场景")
    @LogRecord(description = "发送验证码", module = "用户管理", operationType = LogRecord.OperationType.OTHER)
    @SentinelResource(value = "sendVerificationCode",
            fallback = "fallBackHandle",
            fallbackClass = SentinelBlockHandler.class,
            blockHandler = "hotBackHandle",
            blockHandlerClass = SentinelBlockHandler.class)
    public ResponseVo<Void> sendVerificationCode(@RequestBody @Valid VerificationCodeDTO codeDTO) {
        log.info("发送验证码: mobile={}, type={}", codeDTO.getMobile(), codeDTO.getType());
        return userService.sendVerificationCode(codeDTO);
    }

    /**
     * 获取客户端IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 多个代理的情况，第一个IP为客户端真实IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        return ip;
    }

    /**
     * 验证Token是否有效
     *
     * @param userId 用户ID
     * @param token  Token内容
     * @return 是否有效
     */
    @GetMapping("/token/validate")
    @Operation(summary = "验证Token是否有效", description = "供网关调用，验证指定用户的Token是否有效")
    public boolean validateToken(@RequestParam("userId") Long userId,
                                 @RequestParam("token") String token) {
        log.info("验证Token: userId={}, token={}", userId, token);
        return userService.validateToken(userId, token);
    }
} 